﻿namespace Jsl.FxCop
{
    using System;
    using Microsoft.FxCop.Sdk;
    using Microsoft.VisualStudio.TestTools.UnitTesting;

    [TestClass]
    public class TestDisposeComponentsFieldCorrectly : RuleTestBase<DisposeComponentsFieldCorrectly>
    {
        [TestMethod]
        public void Dispose_is_correct()
        {
            string code = @"
using System.ComponentModel;
using System.Windows.Forms;

public class Class : Form
{
    private IContainer components;

    protected override void Dispose(bool disposing)
    {
        if (this.components != null)
        {
            this.components.Dispose();
        }
    }
}";

            var problems = RunMethodRule(code, true);
            ProblemAssert.That(problems);

            problems = RunMethodRule(code, false);
            ProblemAssert.That(problems);
        }

        [TestMethod]
        public void Must_dispose_components_field()
        {
            string code = @"
using System.ComponentModel;
using System.Windows.Forms;

public class Class : Form
{
    private IContainer components;

    protected override void Dispose(bool disposing)
    {
        if (this.components != null)
        {
        }
    }
}";

            new bool[] { true, false }
                .Run(debugMode =>
                {
                    var problems = RunMethodRule(code, debugMode);

                    ProblemAssert.That(
                        problems,
                        100,
                        FixCategories.NonBreaking,
                        MessageLevel.Error,
@"Modify the code to dispose the components field like this:
if (disposing)
{
  if (this.components != null)
  {
    this.components.Dispose();
  }
}");
                });
        }

        [TestMethod]
        public void Should_only_dispose_once()
        {
            string code = @"
using System.ComponentModel;
using System.Windows.Forms;

public class Class : Form
{
    private IContainer components;

    protected override void Dispose(bool disposing)
    {
        if (this.components != null)
        {
            this.components.Dispose();
        }

        this.components.Dispose();
    }
}";

            new bool[] { true, false }
                .Run(debugMode =>
                {
                    var problems = RunMethodRule(code, debugMode);

                    ProblemAssert.That(
                        problems,
                        100,
                        FixCategories.NonBreaking,
                        MessageLevel.Error,
                        "There should only be one call to dispose the components field.");
                });
        }

        [TestMethod]
        public void Dispose_must_be_only_statement_in_if()
        {
            string code = @"
using System.ComponentModel;
using System.Windows.Forms;

public class Class : Form
{
    private IContainer components;

    protected override void Dispose(bool disposing)
    {
        if (this.components != null)
        {
            this.components.Dispose();
            this.components = null;
        }
    }
}";

            new bool[] { true, false }
                .Run(debugMode =>
                {
                    var problems = RunMethodRule(code, debugMode);

                    ProblemAssert.That(
                        problems,
                        100,
                        FixCategories.NonBreaking,
                        MessageLevel.Error,
@"The this.components.Dispose() should be the only thing in an if statement:
if (disposing)
{
  if (this.components != null)
  {
    this.components.Dispose();
  }
}");
                });
        }

        [TestMethod]
        public void Needs_null_check_before_disposing()
        {
            string code = @"
using System.ComponentModel;
using System.Windows.Forms;

public class Class : Form
{
    private IContainer components;

    protected override void Dispose(bool disposing)
    {
        this.components.Dispose();
    }
}";

            new bool[] { true, false }
                .Run(debugMode =>
                {
                    var problems = RunMethodRule(code, debugMode);

                    ProblemAssert.That(
                        problems,
                        100,
                        FixCategories.NonBreaking,
                        MessageLevel.Error,
@"The this.components.Dispose() should be the only thing in an if statement:
if (disposing)
{
  if (this.components != null)
  {
    this.components.Dispose();
  }
}");
                });
        }

        #region Private Methods
        private static ProblemCollection RunMethodRule(string code, bool debugMode)
        {
            var assembly = new CompileHelper()
                .AddAssemblyReference("System.Windows.Forms.dll")
                .AddSource(code)
                .Generate(debugMode);
            var type = TypeNodeHelper.GetTypeNode(assembly, "Class");

            return RunMethodRule(type, "Dispose");
        }
        #endregion Private Methods
    }
}